/****************************************************************************
|*  DirectLighting.cpp
|*
|*  A integrator only considering direct lighting
|*
\***********************************************************/

#include "DirectLighting.h"

#include <utils/Vector4.h>
#include <Scene.h>

#define _USE_MATH_DEFINES
#include <math.h>

#include <utils/MonteCarloUtilities.h>
#include <sceneelements/geometry/MeshTriangle.h>
#include <cstdio>

DirectLighting::DirectLighting(){}

Vector4 DirectLighting::integrate( const Ray& ray )
{
	return integrateSamplingBRDF(ray);
}

Vector4 DirectLighting::integrateConstant(const Ray& ray) {
	IntersectionData* iData = m_scene->intersect(ray);
	Vector4 color;

	if(iData) { // successful intersection test
		color = iData->material->diffuse;
	} else color = m_scene->getBackground();

	delete iData;

	return color.clamp01();
}

Vector4 DirectLighting::integrateSamplingBRDF( const Ray& ray )
{
	Vector4 color; // The scene bg is assumed to have emission zero
	IntersectionData* iData = m_scene->intersect(ray);

	if(iData) { // successful intersection test
		Vector3 p = iData->position;
		int N = m_nSamples;
		Vector4 sum;

		color = iData->material->emission;
		/*
		// This will apply only to light sources and it
		// appears to have no effect.
		Vector3 d1 = ray.direction;
		Vector3 d2 = iData->surfaceNormal;
		color *= fabs(d1.dot(d2));
		*/

		assert(N > 0);
		for(int i = 0; i < N; ++i) {
			Vector3 s = MonteCarloUtilities::uniformSampleHemisphere(
					iData->surfaceNormal);
			Ray r = Ray(p, s);
			r.min_t = Ray::epsilon_t;

			IntersectionData *iData2 = m_scene->intersect(r);
			if(iData2) {
				Vector4 L = iData2->material->emission;
				// check for correct side of light emission
				if(s.dot(iData2->surfaceNormal) < 0) {
					double cos_th = fabs(s.dot(iData->surfaceNormal));
					L *= cos_th;
					sum += L;
				}
			}
			delete iData2;
		}
		sum *= 2*M_PI/(double)N;
		// The division by M_PI is done in order to conserve the energy
		// as seen on the CS-440 forum
		// or at: http://www.rorydriscoll.com/2009/01/25/energy-conservation-in-games/
		sum = sum.componentMul(iData->material->diffuse/M_PI);
		color += sum;
	}

	color[3] = 1; // We don't consider the alpha channel

	// Cleanup
	delete iData; // it seems we have to free this ourselves

	return color.clamp01();
}

void DirectLighting::setScene( Scene* scene )
{
	m_scene  = scene;
}
